#include <sqlite3.h>
#include <string>
#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

bool CreateData()
{
    sqlite3 *dbHandle = nullptr;
    int s32Ret = sqlite3_open("test1.db", &dbHandle);
    if (s32Ret != SQLITE_OK)
    {
        sqlite3_close(dbHandle);
        return false;
    }

    //创建数据表
    const char *sqlCreate = "CREATE TABLE if not exists PersonTable(ID INTERG NOT NULL, Name Text, Address BLOB);";
    s32Ret = sqlite3_exec(dbHandle, sqlCreate, NULL, NULL, NULL);
    std::cout << s32Ret << std::endl;

    //插入数据
    sqlite3_stmt *stmt = NULL;
    const char *sqlInsert = "INSERT INTO PersonTable(ID, Name, Address) VALUES(?, ?, ?);";
    int id = 2;
    const char *name = "Peter";

    //解析并保存 SQL 脚本
    s32Ret = sqlite3_prepare_v2(dbHandle, sqlInsert, strlen(sqlInsert), &stmt, nullptr);
    if (s32Ret != SQLITE_OK)
    {
        //输出错误消息
        std::cout << sqlite3_errmsg(dbHandle) << std::endl;
        //删除一个准备好的语句。
        sqlite3_finalize(stmt);
        //关闭数据库
        sqlite3_close(dbHandle);
        return false;
    }

    for (int i = 0; i < 10; ++i)
    {
        id++;
        //绑定参数
        sqlite3_bind_int(stmt, 1, id);
        sqlite3_bind_text(stmt, 2, name, strlen(name), SQLITE_TRANSIENT);
        sqlite3_bind_null(stmt, 3); //结束标志位吗？

        //执行SQL 语句
        if (sqlite3_step(stmt) != SQLITE_DONE)
        {
            std::cout << sqlite3_errmsg(dbHandle) << std::endl;
            break;
        }

        //重新初始化 sqlite3_stmt 对象下次再用
        //将准备好的语句对象重置为初始状态，准备重新执行。使用sqlite3_bind_*（）API绑定的值的任何SQL语句变量都保留其值。使用sqlite3_clear_bindings重置绑定。
        sqlite3_reset(stmt);
    }

    sqlite3_finalize(stmt);
    sqlite3_close(dbHandle);

    return s32Ret == SQLITE_OK;
}

/**
 * 对于带参数的 SQL 语句，我们先要定义 sqlite_stmt, 它会保存 SQL 语句，具体是通过  sqlite3_prepare_v2 函数解析并保存SQL  语句。在 sqlite3_prepare_v2
 * 解析SQL语句之后还要绑定参数，根据参数的类型调用不同的 sqlite3_bind_xxx 函数来绑定，在绑定完参数之后，再调用  sqlite3_step 来执行SQL 操作，
 * 最后要释放 sqlite_stmt 指针。这里需要注意的是，不需要每次都定义一个 sqlite3_stmt 指针，可以重复使用 sqlite3_stmt 指针以提高效率，每次用完 sqlite3_stmt 
 * 指针后，重置一下就可以了。在插入数据完成之后，还要释放 sqlite3_stmt 指针，否则会内存泄.
 * */

bool GetData()
{
    sqlite3 *dbHandle = nullptr;
    int s32Ret = sqlite3_open("test1.db", &dbHandle);
    if (s32Ret != SQLITE_OK)
    {
        sqlite3_close(dbHandle);
        return false;
    }

    const char *sqlQuery = "SELECT * FROM PersonTable;";
    sqlite3_stmt *stmt = nullptr;
    s32Ret = sqlite3_prepare_v2(dbHandle, sqlQuery, strlen(sqlQuery), &stmt, nullptr);
    if (s32Ret != SQLITE_OK)
    {
        //输出错误消息
        std::cout << sqlite3_errmsg(dbHandle) << std::endl;
        //删除一个准备好的语句。
        sqlite3_finalize(stmt);
        //关闭数据库
        sqlite3_close(dbHandle);
        return false;
    }

    int s32Column = sqlite3_column_count(stmt); //一行有多少个元素
    while (true)
    {
        int s32Ret = sqlite3_step(stmt);
        if (s32Ret == SQLITE_DONE) //语句执行完毕
        {
            cout << "SQLITE_DONE" << endl;
            break;
        }

        if (s32Ret != SQLITE_ROW) //在执行期间出错
        {
            cout << "SQLITE_ROW" << endl;
            break;
        }

        for (int i = 0; i < s32Column; ++i)
        {
            int colType = sqlite3_column_type(stmt, i);
            if (colType == SQLITE_INTEGER)
            {
                int val = sqlite3_column_int(stmt, i);
                cout << "int value : " << val << endl;
            }
            else if (colType == SQLITE_FLOAT)
            {
                double val = sqlite3_column_double(stmt, i);
                cout << "double value : " << val << endl;
            }
            else if (colType == SQLITE_TEXT)
            {
                const u_char *val = sqlite3_column_text(stmt, i);
                cout << "text val : " << val << endl;
            }
            else if (colType == SQLITE_NULL)
            {
                cout << "null type" << endl;
            }
        }
    }

    sqlite3_reset(stmt);
    sqlite3_close(dbHandle);
    sqlite3_finalize(stmt);

    return true;
}
/**
 * 在实际使用时，需要循环读取每行的 buffer，然后从 buffer 中取得每一列的值
 * sqlite3_step 每次执行 获得一行数据
 * sqlite3_column_count 获取 每行的 元素个数, 
 * */

int main(void)
{
    CreateData();
    GetData();

    return 0;
}
